// bslstl_function_smallobjectoptimization.h                          -*-C++-*-
#ifndef INCLUDED_BSLSTL_FUNCTION_SMALLOBJECTOPTIMIZATION
#define INCLUDED_BSLSTL_FUNCTION_SMALLOBJECTOPTIMIZATION

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide small-object optimization buffer for 'bsl::function'.
//
//@CLASSES:
//  bslstl::Function_SmallObjectOptimization: impl utils for 'bsl::function'.
//
//@SEE_ALSO: bslstl_function
//
//@DESCRIPTION:  This private, subordinate component provides a utility
// 'struct', 'bslstl::Function_SmallObjectOptimization', that provides a suite
// of types, type traits, and constants used in the implementation of
// 'bsl::function', and its "small-object optimization" in particular.

#include <bslscm_version.h>

#include <bslmf_integralconstant.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isnothrowmoveconstructible.h>

#include <bsls_alignmentutil.h>

#include <cstddef>  // 'std::size_t'

namespace BloombergLP {
namespace bslstl {

                   // ======================================
                   // class Function_SmallObjectOptimization
                   // ======================================

class Function_SmallObjectOptimization {
    // This utility 'struct' provides a namespace for several types, type
    // traits, and constants used in the implementation of the small-object
    // optimization for 'bsl::function'.

    // PRIVATE TYPES
    class Dummy;
        // 'Dummy' is an incomplete type that this class uses to declare
        // pointers to member functions and pointers to member data.

    typedef bsls::AlignmentUtil::MaxAlignedType MaxAlignedType;
        // 'MaxAlignedType' is an alias to a type that has the maximum
        // alignment, and is not over-aligned, for the current platform.

  public:
    // TYPES
    union InplaceBuffer {
        // This 'union' defines the storage area for a functor representation.
        // The design uses the small-object optimization in an attempt to avoid
        // allocations for objects that are no larger than 'InplaceBuffer'.
        // When the target object is no larger than 'InplaceBuffer', the
        // small-object optimization can be used by storing the target directly
        // within the 'InplaceBuffer'.
        //
        // Note that union members other than 'd_object_p' are just fillers to
        // make sure that a function or member function pointer can fit without
        // allocation, and that 'InplaceBuffer' has maximum alignment.  The
        // 'd_minbuf' member ensures that 'InplaceBuffer' is large enough to
        // hold modestly complex functors, like 'bdlf::Bind' objects and other
        // functors that store embedded arguments, eliminating the need to
        // allocate memory from the heap for the footprint of such objects.
        //
        // The size of this type ensures that the inplace buffer will be 6
        // pointers in size, and the total footprint of a 'bsl::function'
        // object on most platforms will be 10 pointers, which matches the
        // sizes of previous implementations of 'bdef_Function'.

        // PUBLIC DATA
        void                  *d_object_p;     // pointer to external rep
        void                 (*d_func_p)();    // pointer to function
        void          (Dummy::*d_memFunc_p)(); // pointer to member function
        int            Dummy::*d_memData_p;    // pointer to member data
        MaxAlignedType         d_align;        // force alignment
        void                  *d_minbuf[6];    // force minimum size
    };

    // CONSTANTS
    static const std::size_t k_NON_SOO_SMALL_SIZE = ~sizeof(InplaceBuffer);
        // 'SooFuncSize' (below) adds this value to the size of a small
        // stateful functor to indicate that, despite being small, it should
        // not be allocated inplace using the small object optimization (SOO),
        // i.e., because it does not have a nothrow move constructor and
        // cannot, therefore, be swapped safely.  When a size larger than this
        // constant is seen, the actual object size can be determined by
        // subtracting this constant.  A useful quality of this encoding is
        // that if 'SZ <= sizeof(InplaceBuffer)' for some object size 'SZ',
        // then 'SZ + k_NON_SOO_SMALL_SIZE > sizeof(InplaceBuffer)', thus
        // 'SooFuncSize' (below) for any object that should not be allocated
        // inplace is larger than 'sizeof(InplaceBuffer)', and the
        // 'SooFuncSize' for any object that *should* be allocated inplace is
        // smaller than or equal to 'sizeof(InplaceBuffer)', making the test
        // for "is inplace function" simple.  Note that it is assumed that no
        // actual object has a size larger than this constant.

    // TYPES
    template <class TP>
    class SooFuncSize {
        // This class template provides a metafunction that 'bsl::function'
        // uses to determine the size of an object, and whether to store it
        // using the small-object optimization (SOO) or not.  The 'value'
        // member of this class encodes the size of the specified 'TP' type as
        // follows:
        //
        //: 1 If 'TP' is larger than 'InplaceBuffer', then
        //:   'value == sizeof(TP)'.
        //:
        //: 2 Otherwise, if 'TP' has a non-throwing destructive move (i.e.,
        //:   it is bitwise movable or has a 'noexcept' move constructor),
        //:   then 'value == sizeof(TP)'.
        //:
        //: 3 Otherwise, 'value == sizeof(TP) + k_NON_SOO_SMALL_SIZE'.  This
        //:   encoding indicates that move might throw and, therefore, 'TP'
        //:   should not be allocated in place even though it would fit in the
        //:   footprint of an 'InplaceBuffer'.
        //
        // Note that the 'Soo' prefix is used to indicate that an identifier
        // uses the above protocol.  Thus, a variable called 'SooSize' is
        // assumed to be encoded as above, whereas a variable called 'size'
        // can generally be assumed not to be encoded that way.

        // PRIVATE CONSTANTS
        static const std::size_t k_SOO_ENCODING_OFFSET =
                sizeof(TP) > sizeof(InplaceBuffer)               ? 0 :
                bsl::is_nothrow_move_constructible<TP>::value    ? 0 :
                bslmf::IsBitwiseMoveable<TP>::value              ? 0 :
                k_NON_SOO_SMALL_SIZE;
            // This constant is 'k_NON_SOO_SMALL_SIZE' if 'TP' is small enough
            // to fit within the footprint of 'InplaceBuffer' but should not be
            // placed there because it is neither bitwise movable nor
            // nothrow-move constructible; otherwise this constant is zero.

      public:
        // CONSTANTS
        static const std::size_t value = sizeof(TP) + k_SOO_ENCODING_OFFSET;
            // 'value' encodes the size of the 'TP' type using the algorithm
            // defined in the documentation for this class.
    };

    // TYPES
    template <class FN>
    struct IsInplaceFunc
    : bsl::integral_constant<bool,
                             SooFuncSize<FN>::value <= sizeof(InplaceBuffer)> {
        // This class is a Boolean metafunction that determines whether or not
        // the specified 'FN' template parameter should be allocated within the
        // footprint of the 'InplaceBuffer' (i.e., using the small-object
        // optimization.)
        //
        ///Implementation Note
        ///-------------------
        // In the future, 'InplaceFunc' should also consider the alignment of
        // 'FN' in its determination of whether or not 'bsl::function' will use
        // the small-object optimization.  However, 'bsl::function' currently
        // has no way to specify alignment when it allocates memory.
    };
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

             // ---------------------------------------------------
             // class Function_SmallObjectOptimization::SooFuncSize
             // ---------------------------------------------------

// PRIVATE CONSTANTS
template <class TP>
const std::size_t
    Function_SmallObjectOptimization::SooFuncSize<TP>::k_SOO_ENCODING_OFFSET;

// CONSTANTS
template <class TP>
const std::size_t Function_SmallObjectOptimization::SooFuncSize<TP>::value;

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2020 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
